package guid

import (
	"context"
	"os"
	"sync"
	"time"

	"github.com/mars/internal/pkg/utils"
	"github.com/mars/pkg/loggers"
)

// IdGetter 定义唯一Id获取接口
type IdGetter interface {
	Get() int64
}

type GetterFunc func() int64

func (fn GetterFunc) Get() int64 {
	return fn()
}

const (
	timeBit     = 41
	workBits    = 8
	sequenceBit = 14

	workShift = sequenceBit
	timeShift = workBits + sequenceBit

	sequenceMask = 1<<sequenceBit - 1
)

// Snowflake 基于雪花算法实现id生成策略
type Snowflake struct {
	lock sync.Mutex

	squence       int
	workId        int
	epoch         int64
	lastTimestamp int64
}

func NewSnowflake(holder WorkIdHolderFunc) *Snowflake {
	snow := &Snowflake{epoch: utils.TimeToUnixMilli(time.Date(1996, 10, 29, 03, 0, 0, 87, time.Local))}

	if workId, err := holder.WorkId(context.TODO()); err != nil {
		loggers.Errorf("snowflake generate workId: %s", err)
		os.Exit(1)
	} else {
		snow.workId = workId
	}

	return snow
}

func (snow *Snowflake) Get() int64 {
	var timestamp = utils.UnixMilli()

	snow.lock.Lock()
	defer snow.lock.Unlock()

	if timestamp <= snow.lastTimestamp {
		snow.squence = (snow.squence + 1) & sequenceMask
		if snow.squence == 0 {
			timestamp = snow.waitNextMillSecond(snow.lastTimestamp)
		}
	} else {
		snow.squence = 0
	}

	snow.lastTimestamp = timestamp
	return (timestamp-snow.epoch)<<timeShift | int64(snow.workId)<<workShift | int64(snow.squence)
}

func (snow *Snowflake) waitNextMillSecond(lastTimestamp int64) int64 {
	timestamp := utils.UnixMilli()
	for timestamp < lastTimestamp {
		time.Sleep(time.Millisecond * time.Duration(lastTimestamp-timestamp))
		timestamp = utils.UnixMilli()
	}
	return timestamp
}
